using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using NUnit.Framework;

namespace Clarius.PresentationModel.Design.Tests
{
	[TestFixture]
	public class DataBindingFixture
	{
		private static MockBase CreateInstance()
		{
			return new MockBase();
		}

		[Test]
		public void ShouldInitializeObject()
		{
			object obj = CreateInstance();
			ISupportInitialize init = (ISupportInitialize)obj;
			ISupportInitializeNotification notify = (ISupportInitializeNotification)obj;

			Assert.IsFalse(notify.IsInitialized);

			bool called = false;
			notify.Initialized += delegate { called = true; };

			init.BeginInit();
			init.EndInit();

			Assert.IsTrue(called);
			Assert.IsTrue(notify.IsInitialized);
		}

		[Test]
		public void ShouldIsChangedStartFalse()
		{
			object obj = CreateInstance();
			IChangeTracking change = (IChangeTracking)obj;

			Assert.IsFalse(change.IsChanged);
		}

		[Test]
		public void ShouldIsChangedTrueOnPropertySet()
		{
			MockBase obj = new MockBase();
			IChangeTracking change = (IChangeTracking)obj;

			obj.BaseIntValue = 5;

			Assert.IsTrue(change.IsChanged);
		}

		[Test]
		public void ShouldIsChangedFalseAfterInitializeNoErrors()
		{
			MockBase obj = new MockBase();
			IChangeTracking change = (IChangeTracking)obj;

			obj.BaseIntValue = 5;

			obj.Initialize();

			Assert.IsFalse(change.IsChanged);
		}

		[Test]
		public void ShouldIsChangedTrueIfNestedChanged()
		{
			MockBase obj = new MockBase();
			IChangeTracking change = (IChangeTracking)obj;

			Assert.IsFalse(change.IsChanged);

			obj.BaseObject = new MockBase();

			Assert.IsTrue(change.IsChanged);

			obj.Initialize();

			Assert.IsFalse(change.IsChanged);

			((MockBase)obj.BaseObject).BaseIntValue = 5;

			Assert.IsTrue(change.IsChanged);
		}

		[Test]
		public void ShouldIsChangedFalseOnNestedIfAcceptChangesOnParent()
		{
			MockBase obj = new MockBase();
			obj.BaseObject = new MockBase();
			obj.Initialize();

			((MockBase)obj.BaseObject).BaseIntValue = 5;

			Assert.IsTrue(((MockBase)obj.BaseObject).IsChanged);

			obj.AcceptChanges();

			Assert.IsFalse(((MockBase)obj.BaseObject).IsChanged);
		}

		[Test]
		public void ShouldIsInitializedFalseIfChildNotInitialized()
		{
			MockBase obj = new MockBase();
			ISupportInitialize init = (ISupportInitialize)obj;
			ISupportInitializeNotification notify = (ISupportInitializeNotification)obj;

			init.BeginInit();
			init.EndInit();

			Assert.IsTrue(notify.IsInitialized);

			obj.BaseObject = new MockBase();

			Assert.IsFalse(notify.IsInitialized);

			ISupportInitialize childInit = (ISupportInitialize)obj.BaseObject;
			childInit.BeginInit();
			childInit.EndInit();

			Assert.IsTrue(notify.IsInitialized);
		}

		[ExpectedException(typeof(InvalidOperationException))]
		[Test]
		public void ShouldThrowIfEndInitCalledBeforeBeginInit()
		{
			ISupportInitialize init = (ISupportInitialize)CreateInstance();

			init.EndInit();
		}

		[Test]
		public void ShouldRaiseTypedPropertyChanged()
		{
			MockBase obj = new MockBase();
			bool called = false;
			obj.BaseIntValueChanged += delegate { called = true; };

			obj.BaseIntValue = 5;

			Assert.IsTrue(called);
		}

		[Test]
		public void ShouldRaiseTypedPropertyChangedForReadOnlyPublicProperty()
		{
			MockBase obj = new MockBase();
			bool called = false;
			obj.CalculatedPropertyChanged += delegate { called = true; };

			obj.BaseIntValue = 5;

			Assert.IsTrue(called);
		}

		[Test]
		public void ShouldRaiseGenericPropertyChanged()
		{
			MockBase obj = new MockBase();
			bool called = false;
			obj.PropertyChanged += delegate { called = true; };

			obj.BaseIntValue = 5;

			Assert.IsTrue(called);
		}

		[Test]
		public void ShouldReportNestedChangedProperties()
		{
			MockDerived derived = new MockDerived();
			derived.DerivedIntValue = 5;

			Assert.IsTrue(((IChangeTracking)derived).IsChanged);

			derived.Initialize();
			Assert.IsFalse(((IChangeTracking)derived).IsChanged);

			derived.DerivedObject = new MockBase();
			Assert.IsTrue(((IChangeTracking)derived).IsChanged);

			derived.Initialize();
			Assert.IsFalse(((IChangeTracking)derived).IsChanged);

			((MockBase)derived.DerivedObject).BaseIntValue = 10;
			Assert.IsTrue(((IChangeTracking)derived).IsChanged);
		}

		[Test]
		public void ShouldRaisePropertyChangedForNestedChanges()
		{
			MockDerived derived = new MockDerived();
			derived.DerivedIntValue = 5;
			derived.DerivedObject = new MockBase();

			bool raised = false;

			derived.PropertyChanged += (sender, args) => raised = true;

			((MockBase)derived.DerivedObject).BaseIntValue = 10;

			Assert.IsTrue(raised);
		}

		[Test]
		public void ShouldDetachRaisePropertyChangedForNestedObjectWhenRemoved()
		{
			var derived = new MockDerived();
			var child = new ChildNotifier();

			derived.DerivedIntValue = 5;
			derived.DerivedObject = child;

			bool raised = false;

			derived.PropertyChanged += (sender, args) => raised = true;

			child.Value = 10;

			Assert.IsTrue(raised);

			derived.DerivedObject = null;
			raised = false;

			child.Value = 5;

			Assert.IsFalse(raised);
		}

		[Test]
		public void ShouldReportNestedIsInitialized()
		{
			MockDerived derived = new MockDerived();

			Assert.IsFalse(((ISupportInitializeNotification)derived).IsInitialized);

			derived.Initialize();
			Assert.IsTrue(((ISupportInitializeNotification)derived).IsInitialized);

			derived.DerivedObject = new MockBase();
			Assert.IsFalse(((ISupportInitializeNotification)derived).IsInitialized);

			derived.Initialize();
			Assert.IsTrue(((ISupportInitializeNotification)derived).IsInitialized);
		}
	}
}
